﻿namespace UnityEditor
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using UnityEngine;

    public abstract class PropertyDrawer
    {
        private PropertyAttribute m_Attribute;
        internal static Dictionary<string, PropertyAttribute> s_BuiltinAttributes = null;
        internal static Dictionary<System.Type, System.Type> s_DrawerTypeForType = null;
        internal static Dictionary<string, PropertyDrawer> s_PropertyDrawers = new Dictionary<string, PropertyDrawer>();

        protected PropertyDrawer()
        {
        }

        private static void AddBuiltinAttribute(string componentTypeName, string propertyPath, PropertyAttribute attr)
        {
            string key = componentTypeName + "_" + propertyPath;
            s_BuiltinAttributes.Add(key, attr);
        }

        private static void BuildDrawerTypeForTypeDictionary()
        {
            s_DrawerTypeForType = new Dictionary<System.Type, System.Type>();
            IEnumerator<System.Type> enumerator = EditorAssemblies.SubclassesOf(typeof(PropertyDrawer)).GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    System.Type current = enumerator.Current;
                    foreach (CustomPropertyDrawer drawer in current.GetCustomAttributes(typeof(CustomPropertyDrawer), true))
                    {
                        s_DrawerTypeForType[drawer.type] = current;
                    }
                }
            }
            finally
            {
                if (enumerator == null)
                {
                }
                enumerator.Dispose();
            }
        }

        private static PropertyAttribute GetBuiltinAttribute(SerializedProperty property)
        {
            if (property.serializedObject.targetObject == null)
            {
                return null;
            }
            System.Type type = property.serializedObject.targetObject.GetType();
            if (type == null)
            {
                return null;
            }
            string key = type.Name + "_" + property.propertyPath;
            PropertyAttribute attribute = null;
            s_BuiltinAttributes.TryGetValue(key, out attribute);
            return attribute;
        }

        internal static PropertyDrawer GetDrawer(SerializedProperty property)
        {
            if (property != null)
            {
                PropertyDrawer drawer;
                if (property.serializedObject.inspectorMode != InspectorMode.Normal)
                {
                    return null;
                }
                string propertyString = GetPropertyString(property);
                if (s_PropertyDrawers.TryGetValue(propertyString, out drawer))
                {
                    return drawer;
                }
                System.Type type = null;
                PropertyAttribute fieldAttribute = null;
                UnityEngine.Object targetObject = property.serializedObject.targetObject;
                if ((targetObject is MonoBehaviour) || (targetObject is ScriptableObject))
                {
                    fieldAttribute = GetFieldAttribute(GetFieldInfoFromProperty(property, out type));
                }
                else
                {
                    if (s_BuiltinAttributes == null)
                    {
                        PopulateBuiltinAttributes();
                    }
                    if (fieldAttribute == null)
                    {
                        fieldAttribute = GetBuiltinAttribute(property);
                    }
                }
                System.Type drawerTypeForType = null;
                if (fieldAttribute != null)
                {
                    drawerTypeForType = GetDrawerTypeForType(fieldAttribute.GetType());
                }
                if ((drawerTypeForType == null) && (type != null))
                {
                    drawerTypeForType = GetDrawerTypeForType(type);
                }
                if (drawerTypeForType != null)
                {
                    drawer = (PropertyDrawer) Activator.CreateInstance(drawerTypeForType);
                    drawer.m_Attribute = fieldAttribute;
                    s_PropertyDrawers[propertyString] = drawer;
                    return drawer;
                }
                s_PropertyDrawers[propertyString] = null;
            }
            return null;
        }

        private static System.Type GetDrawerTypeForType(System.Type type)
        {
            if (s_DrawerTypeForType == null)
            {
                BuildDrawerTypeForTypeDictionary();
            }
            System.Type type2 = null;
            s_DrawerTypeForType.TryGetValue(type, out type2);
            return type2;
        }

        private static PropertyAttribute GetFieldAttribute(System.Reflection.FieldInfo field)
        {
            if (field != null)
            {
                object[] customAttributes = field.GetCustomAttributes(typeof(PropertyAttribute), true);
                if ((customAttributes != null) && (customAttributes.Length > 0))
                {
                    return (PropertyAttribute) customAttributes[0];
                }
            }
            return null;
        }

        private static System.Reflection.FieldInfo GetFieldInfoFromProperty(SerializedProperty property, out System.Type type)
        {
            System.Type scriptTypeFromProperty = GetScriptTypeFromProperty(property);
            if (scriptTypeFromProperty == null)
            {
                type = null;
                return null;
            }
            return GetFieldInfoFromPropertyPath(scriptTypeFromProperty, property.propertyPath, out type);
        }

        private static System.Reflection.FieldInfo GetFieldInfoFromPropertyPath(System.Type host, string path, out System.Type type)
        {
            System.Reflection.FieldInfo field = null;
            type = host;
            char[] separator = new char[] { '.' };
            string[] strArray = path.Split(separator);
            for (int i = 0; i < strArray.Length; i++)
            {
                string name = strArray[i];
                if (((i < (strArray.Length - 1)) && (name == "Array")) && strArray[i + 1].StartsWith("data["))
                {
                    if (type.IsArray)
                    {
                        type = type.GetElementType();
                    }
                    else if (type.GetGenericTypeDefinition() == typeof(List<>))
                    {
                        type = type.GetGenericArguments()[0];
                    }
                    field = null;
                    i++;
                }
                else
                {
                    field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                    if (field == null)
                    {
                        return null;
                    }
                    type = field.FieldType;
                }
            }
            return field;
        }

        public virtual float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            return 16f;
        }

        private static string GetPropertyString(SerializedProperty property)
        {
            if (property.serializedObject.targetObject == null)
            {
                return string.Empty;
            }
            return (property.serializedObject.targetObject.GetInstanceID() + "_" + property.arrayIndexLessPropertyPath);
        }

        private static System.Type GetScriptTypeFromProperty(SerializedProperty property)
        {
            SerializedProperty property2 = property.serializedObject.FindProperty("m_Script");
            if (property2 == null)
            {
                return null;
            }
            MonoScript objectReferenceValue = property2.objectReferenceValue as MonoScript;
            if (objectReferenceValue == null)
            {
                return null;
            }
            return objectReferenceValue.GetClass();
        }

        public virtual void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            EditorGUI.LabelField(position, label, EditorGUIUtility.TempContent("No GUI Implemented"));
        }

        private static void PopulateBuiltinAttributes()
        {
            s_BuiltinAttributes = new Dictionary<string, PropertyAttribute>();
            AddBuiltinAttribute("Label", "m_Text", new MultilineAttribute(5));
        }

        public PropertyAttribute attribute
        {
            get
            {
                return this.m_Attribute;
            }
        }
    }
}

